﻿Imports System.Text
Imports FastColoredTextBoxNS
Imports System.Text.RegularExpressions
Imports System.Collections.Specialized

Public Class frmMain
    Private Const REGEX_FORMAT_IDENT As String = "{(\d+)(,[-+]?\d+)?(:\w+)?}"
    Private Const REGEX_FORMAT_NUMBER As String = "{(\d+)[,:}]"

    ' 用于绘制关键字的STYLE
    Private ReadOnly m_oFormatterStyles As List(Of Style)
    Private ReadOnly m_oSelectStyles As List(Of Style)
    Private ReadOnly m_oBackColors As List(Of Color)

    ' 用于统计的样式使用率
    Private FormatterArguemntCount As Integer

    ' 表示单机的是那个表头
    Private CurrentHeader As DataGridViewColumn

    ' 表示原始的状态
    Private OriginalBarStatus As New IO.MemoryStream
    Private OriginalDockStatus As New IO.MemoryStream


    Private Sub frmMain_FormClosed(ByVal sender As Object, ByVal e As FormClosedEventArgs) Handles Me.FormClosed
        With My.MySettings.Default
            If WindowState = FormWindowState.Maximized Then
                .MainWindowMaximized = True
            Else
                .MainWindowSize = Size
                .MainWindowMaximized = False
            End If

            If .History Is Nothing Then
                .History = New StringCollection
            Else
                .History.Clear()
            End If

            For Each s As String In rcboHistory.Items
                .History.Add(s)
            Next

            .DataSource = GetGirdValue()
            .Save()
        End With

        bmDefault.SaveLayoutToXml("codegen.bar")
        dmMain.SaveLayoutToXml("codegen.dck")
    End Sub

    Private Sub frmMain_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        ' 加载配置文件
        If My.MySettings.Default.MainWindowMaximized Then
            Me.WindowState = FormWindowState.Maximized
        Else
            Me.Size = My.MySettings.Default.MainWindowSize
        End If

        bmDefault.SaveLayoutToStream(OriginalBarStatus)
        dmMain.SaveLayoutToStream(OriginalDockStatus)

        If System.IO.File.Exists("codegen.bar") Then
            bmDefault.RestoreLayoutFromXml("codegen.bar")
        End If

        If System.IO.File.Exists("codegen.dck") Then
            dmMain.RestoreLayoutFromXml("codegen.dck")
        End If

        ' 加载历史记录
        If My.MySettings.Default.History IsNot Nothing Then
            rcboHistory.Items.AddRange(My.MySettings.Default.History)
        End If

        ' 加载语言状态
        Select Case My.MySettings.Default.Language
            Case Language.Custom : btnHLNone.Down = True
            Case Language.CSharp : btnHLC.Down = True
            Case Language.SQL : btnHLSql.Down = True
            Case Language.JS : btnHLJs.Down = True
            Case Language.PHP : btnHLPhp.Down = True
        End Select

        ' 加载数据
        If Not String.IsNullOrEmpty(My.MySettings.Default.DataSource) Then
            InsertText(My.MySettings.Default.DataSource)
        End If

        ' 显示本窗口
        Me.Show()
    End Sub

    Public Sub New()

        ' 此调用是 Windows 窗体设计器所必需的。
        InitializeComponent()

        ' 在 InitializeComponent() 调用之后添加任何初始化。

        ' 添加背景颜色
        m_oBackColors = New List(Of Color)
        m_oBackColors.Add(Color.FromArgb(255, Color.Red))
        m_oBackColors.Add(Color.FromArgb(255, Color.Orange))
        m_oBackColors.Add(Color.FromArgb(255, Color.DarkKhaki))
        m_oBackColors.Add(Color.FromArgb(255, Color.Green))
        m_oBackColors.Add(Color.FromArgb(255, Color.DarkCyan))
        m_oBackColors.Add(Color.FromArgb(255, Color.Blue))
        m_oBackColors.Add(Color.FromArgb(255, Color.Purple))
        m_oBackColors.Add(Color.FromArgb(255, Color.Magenta))

        ' 添加Style
        m_oFormatterStyles = New List(Of Style)
        m_oSelectStyles = New List(Of Style)
        For Each c As Color In m_oBackColors
            m_oFormatterStyles.Add(New TextStyle(New SolidBrush(Color.Black), New SolidBrush(ColorBlend(c, Color.White, 0.3)), FontStyle.Bold Or FontStyle.Underline))
            m_oSelectStyles.Add(New TextStyle(New SolidBrush(Color.White), New SolidBrush(ColorBlend(c, Color.Black, 0.2)), FontStyle.Bold Or FontStyle.Underline))
        Next

    End Sub

    Private Sub dgvDataSource_CellFormatting(ByVal sender As Object, ByVal e As DataGridViewCellFormattingEventArgs)
        e.CellStyle.ForeColor = m_oBackColors(e.ColumnIndex Mod m_oBackColors.Count)
    End Sub

    Private Sub btnGenerate_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnGenerate.ItemClick
        ' 对于每一行记录都生成相应的代码
        Dim sb As New StringBuilder
        Dim fmt As String = ftxtPattern.Text

        ' 插入到MRU中
        rcboHistory.Items.Add(fmt, True)

        ' 如果数数据表就按照数据格式添加
        For Each oRow As DataGridViewRow In dgvDataSource.Rows
            If oRow.Index > 0 Then
                sb.AppendLine()
            End If

            Dim oStrArray() As Object = (From oCell As DataGridViewCell In oRow.Cells Select oCell.Value).ToArray()
            Try
                sb.AppendFormat(fmt, oStrArray)
            Catch ex As Exception
                Debug.Print(ex.Message)
            End Try
        Next

        ' 写回
        ftxtResult.Text = sb.ToString()

    End Sub

    Private Sub dgvDataSource_ColumnHeaderMouseClick(ByVal sender As Object, ByVal e As DataGridViewCellMouseEventArgs)
        If e.Button = MouseButtons.Right Then
            CurrentHeader = dgvDataSource.Columns(e.ColumnIndex)
            popDataGridHeader.ShowPopup(Cursor.Position)
        End If
    End Sub

    Private Sub btnOutputCopy_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnOutputCopy.ItemClick
        Clipboard.SetText(ftxtResult.Text)
    End Sub


    Private Sub OnTypeButtonClicked_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnTypeString.ItemClick, btnTypeUInt64.ItemClick, btnTypeUInt32.ItemClick, btnTypeSingle.ItemClick, btnTypeInt64.ItemClick, btnTypeInt32.ItemClick, btnTypeDouble.ItemClick, btnTypeDate.ItemClick, btnTypeDecimal.ItemClick
        If CurrentHeader IsNot Nothing Then
            If e.Item Is btnTypeDecimal Then
                CurrentHeader.ValueType = GetType(Decimal)
            ElseIf e.Item Is btnTypeDate Then
                CurrentHeader.ValueType = GetType(Date)
            ElseIf e.Item Is btnTypeDouble Then
                CurrentHeader.ValueType = GetType(Double)
            ElseIf e.Item Is btnTypeInt32 Then
                CurrentHeader.ValueType = GetType(Integer)
            ElseIf e.Item Is btnTypeInt64 Then
                CurrentHeader.ValueType = GetType(Long)
            ElseIf e.Item Is btnTypeSingle Then
                CurrentHeader.ValueType = GetType(Single)
            ElseIf e.Item Is btnTypeString Then
                CurrentHeader.ValueType = GetType(String)
            ElseIf e.Item Is btnTypeUInt32 Then
                CurrentHeader.ValueType = GetType(UInteger)
            ElseIf e.Item Is btnTypeUInt64 Then
                CurrentHeader.ValueType = GetType(ULong)
            End If
        End If
    End Sub

    ''' <summary>
    ''' 插入文字到数据源中
    ''' </summary>
    ''' <param name="Text"></param>
    ''' <remarks></remarks>
    Private Sub InsertText(ByRef Text As String, ByVal StartRow As Integer, ByVal StartColumn As Integer)
        Dim i As Integer = StartRow
        Dim j As Integer = StartColumn
        Dim saLines = Text.Split(New String() {vbCrLf, vbCr, vbLf}, StringSplitOptions.None)

        ' 如果行数不够就要增加行
        dgvDataSource.RowCount = Math.Max(saLines.Length + StartRow, dgvDataSource.RowCount)

        For Each sLine As String In saLines
            If sLine.Trim().Length <> 0 Then
                Dim sCellValues As String() = sLine.Split(New String() {vbTab}, StringSplitOptions.None)

                ' 如果列不够就扩充列
                dgvDataSource.ColumnCount = Math.Max(dgvDataSource.ColumnCount, sCellValues.Length + StartColumn)

                j = StartColumn

                ' 赋值到列中
                For Each sCellValue As String In sCellValues
                    dgvDataSource.Rows(i).Cells(j).Value = sCellValue
                    j = j + 1
                Next

                ' 更新行首
                dgvDataSource.Rows(i).HeaderCell.Value = i.ToString()

                i = i + 1
            End If
        Next

        For j = StartColumn To dgvDataSource.ColumnCount - 1
            SetColumnStyle(j)
        Next

        dgvDataSource.RowCount = i
        spnRow.EditValue = dgvDataSource.RowCount
        spnColumn.EditValue = dgvDataSource.ColumnCount
    End Sub

    Private Sub InsertText(ByRef Text As String)
        If dgvDataSource.SelectedCells.Count = 0 Then
            InsertText(Text, 0, 0)
        Else
            InsertText(Text, dgvDataSource.SelectedCells(0).RowIndex, dgvDataSource.SelectedCells(0).ColumnIndex)
        End If
    End Sub

    Private Function GetGirdValue() As String
        Dim sb As New StringBuilder

        For Each row As DataGridViewRow In dgvDataSource.Rows
            ' 如果不是第一行就添加换行符
            If row.Index > 0 Then
                sb.AppendLine()
            End If

            ' 添加单元格中的内容
            For Each cell As DataGridViewCell In row.Cells
                ' 如果不是第一个单元格就要添加制表符
                If cell.ColumnIndex > 0 Then
                    sb.Append(vbTab)
                End If

                sb.Append(cell.Value.ToString())
            Next
        Next

        GetGirdValue = sb.ToString()
    End Function

    Private Sub spnRow_EditValueChanged(ByVal sender As Object, ByVal e As EventArgs) Handles spnRow.EditValueChanged
        If spnRow.EditValue > 0 Then
            dgvDataSource.RowCount = spnRow.EditValue
        End If
    End Sub

    Private Sub spnColumn_EditValueChanged(ByVal sender As Object, ByVal e As EventArgs) Handles spnColumn.EditValueChanged
        Dim nOriginalColumnCount = dgvDataSource.ColumnCount
        dgvDataSource.ColumnCount = spnColumn.EditValue
        SetColumnStyle(nOriginalColumnCount - 1, spnColumn.EditValue - 1, False)
    End Sub

    Private Sub btnDataSourcePaste_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnDataSourcePaste.ItemClick
        InsertText(Clipboard.GetText())
    End Sub

    Private Sub mnuEditPaste_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles mnuEditPaste.ItemClick
        If dgvDataSource.Focused Then
            btnDataSourcePaste.PerformClick()
        ElseIf ftxtPattern.Focused Then
            ftxtPattern.Paste()
        End If
    End Sub

    Private Sub mnuEditSelAll_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles mnuEditSelAll.ItemClick
        If dgvDataSource.Focused Then
            dgvDataSource.SelectAll()
        ElseIf ftxtPattern.Focused Then
            ftxtPattern.SelectAll()
        ElseIf ftxtResult.Focused Then
            ftxtResult.SelectAll()
        End If
    End Sub

    Private Sub mnuEditCopy_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles mnuEditCopy.ItemClick
        If dgvDataSource.Focused Then
            CopyToClipboard()
        ElseIf ftxtPattern.Focused Then
            ftxtPattern.Copy()
        ElseIf ftxtResult.Focused Then
            ftxtResult.Copy()
        End If
    End Sub

    ''' <summary>
    ''' 将表格中的数据复制到剪贴板上
    ''' </summary>
    ''' <param name="EraseOriginal"></param>
    ''' <remarks></remarks>
    Private Sub CopyToClipboard(Optional ByVal EraseOriginal As Boolean = False)
        Dim nStartColumn As Integer = Integer.MaxValue
        Dim nStartRow As Integer = Integer.MaxValue
        Dim nEndColumn As Integer = Integer.MinValue
        Dim nEndRow As Integer = Integer.MinValue

        If dgvDataSource.SelectedCells.Count = 0 Then
            Exit Sub
        End If

        ' 计算最大的复制矩阵
        For Each cell As DataGridViewCell In dgvDataSource.SelectedCells
            nStartColumn = Math.Min(cell.ColumnIndex, nStartColumn)
            nStartRow = Math.Min(cell.RowIndex, nStartRow)
            nEndColumn = Math.Max(cell.ColumnIndex, nEndColumn)
            nEndRow = Math.Max(cell.RowIndex, nEndRow)
        Next

        ' 保存项目的值
        Dim sAllSelItem(,) As String
        ReDim sAllSelItem(nEndRow - nStartRow, nEndColumn - nStartColumn)

        For Each cell As DataGridViewCell In dgvDataSource.SelectedCells
            sAllSelItem(cell.RowIndex - nStartRow, cell.ColumnIndex - nStartColumn) = cell.Value.ToString()

            If EraseOriginal Then
                cell.Value = Nothing
            End If
        Next

        ' 组件最后的字符串
        Dim sb As New StringBuilder

        For i As Integer = 0 To UBound(sAllSelItem, 1)
            If i <> 0 Then
                sb.AppendLine()
            End If

            For j As Integer = 0 To UBound(sAllSelItem, 2)
                If j <> 0 Then
                    sb.Append(vbTab)
                End If

                sb.Append(sAllSelItem(i, j))
            Next
        Next

        Clipboard.Clear()
        Clipboard.SetText(sb.ToString())
    End Sub

    Private Sub mnuEditCut_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles mnuEditCut.ItemClick
        If dgvDataSource.Focused Then
            CopyToClipboard(True)
        ElseIf ftxtPattern.Focused Then
            ftxtPattern.Cut()
        End If
    End Sub

    Private Sub SetColumnStyle(ByVal Index As Integer, Optional ByVal IsSelected As Boolean = False)
        If Index >= 0 Then
            dgvDataSource.Columns(Index).HeaderText = String.Format("{{{0}}}", Index)

            If IsSelected Then
                dgvDataSource.Columns(Index).DefaultCellStyle.ForeColor = Color.White
                dgvDataSource.Columns(Index).DefaultCellStyle.BackColor = ColorBlend(m_oBackColors(Index Mod m_oBackColors.Count), Color.Black, 0.3)
            Else
                dgvDataSource.Columns(Index).DefaultCellStyle.ForeColor = Color.Black
                dgvDataSource.Columns(Index).DefaultCellStyle.BackColor = ColorBlend(m_oBackColors(Index Mod m_oBackColors.Count), Color.White, 0.2)
            End If
        End If
    End Sub

    Private Sub SetColumnStyle(ByVal BeginIndex As Integer, ByVal EndIndex As Integer, Optional ByVal IsSelected As Boolean = False)
        For i As Integer = BeginIndex To EndIndex
            SetColumnStyle(i, IsSelected)
        Next
    End Sub

    Private Sub SetColumnStyle(Optional ByVal IsSelected As Boolean = False)
        For i As Integer = 0 To dgvDataSource.ColumnCount - 1
            SetColumnStyle(i, IsSelected)
        Next
    End Sub

    Private Shared Function ColorBlend(ByVal Color1 As Color, ByVal Color2 As Color, ByVal Blend As Double) As Color
        ColorBlend = Color.FromArgb(Color1.A * Blend + Color2.A * (1 - Blend), _
                                    Color1.R * Blend + Color2.R * (1 - Blend), _
                                    Color1.G * Blend + Color2.G * (1 - Blend), _
                                    Color1.B * Blend + Color2.B * (1 - Blend))
    End Function

    Private Sub ftxtPattern_SelectionChangedDelayed(ByVal sender As Object, ByVal e As EventArgs) Handles ftxtPattern.SelectionChangedDelayed
        If Not (m_oFormatterStyles Is Nothing) AndAlso (m_oFormatterStyles.Count > 0) Then
            ' 更新样式
            Dim lrRanges As New List(Of KeyValuePair(Of Range, Integer))
            Dim i As Integer = 0
            Dim nStyleCount As Integer = m_oFormatterStyles.Count
            Dim nSelStart As Integer = ftxtPattern.SelectionStart
            Dim nSelectIndex As Integer = -1

            FormatterArguemntCount = 0

            For Each rng As Range In ftxtPattern.GetRanges(REGEX_FORMAT_IDENT, RegexOptions.IgnoreCase)
                ' 确定位置
                i = Integer.Parse(Regex.Match(rng.Text, REGEX_FORMAT_NUMBER).Groups(1).Value)
                FormatterArguemntCount = Math.Max(i, FormatterArguemntCount)

                ' 确定选择的索引
                If rng.Contains(ftxtPattern.Selection.Start) Then
                    nSelectIndex = i
                End If

                ' 添加到集合中
                lrRanges.Add(New KeyValuePair(Of Range, Integer)(rng, i))
            Next

            ' 开始更新样式
            For Each kv As KeyValuePair(Of Range, Integer) In lrRanges
                kv.Key.ClearStyle(StyleIndex.All)
                If kv.Value = nSelectIndex Then
                    kv.Key.SetStyle(m_oSelectStyles(kv.Value Mod m_oSelectStyles.Count))
                Else
                    kv.Key.SetStyle(m_oFormatterStyles(kv.Value Mod m_oSelectStyles.Count))
                End If
            Next

            ' 更新列
            For Each col As DataGridViewColumn In dgvDataSource.Columns
                SetColumnStyle(col.Index, col.Index = nSelectIndex)
            Next
        End If
    End Sub

    Private Sub ftxtPattern_TextChanged(ByVal sender As Object, ByVal e As FastColoredTextBoxNS.TextChangedEventArgs) Handles ftxtPattern.TextChanged
        ftxtPattern_SelectionChangedDelayed(sender, New EventArgs())
    End Sub

    Private Sub ChangeLanguage(ByVal Lanuage As FastColoredTextBoxNS.Language)
        ftxtPattern.Language = Lanuage
        ftxtResult.Language = Lanuage
        ftxtResult.Range.ClearStyle(FastColoredTextBoxNS.StyleIndex.All)
        ftxtResult.OnSyntaxHighlight(New TextChangedEventArgs(ftxtResult.Range))
        ftxtPattern.Range.ClearStyle(FastColoredTextBoxNS.StyleIndex.All)
        ftxtPattern.OnSyntaxHighlight(New TextChangedEventArgs(ftxtPattern.Range))
        ftxtPattern_SelectionChangedDelayed(Nothing, New System.EventArgs())
    End Sub
    Private Sub btnHLNone_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnHLNone.ItemClick
        ChangeLanguage(Language.Custom)
    End Sub

    Private Sub btnHLC_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnHLC.ItemClick
        ChangeLanguage(Language.CSharp)
    End Sub

    Private Sub btnHLSql_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnHLSql.ItemClick
        ChangeLanguage(Language.SQL)
    End Sub

    Private Sub btnHLHTML_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnHLHTML.ItemClick
        ChangeLanguage(Language.HTML)
    End Sub

    Private Sub btnHLJs_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnHLJs.ItemClick
        ChangeLanguage(Language.JS)
    End Sub

    Private Sub btnHLPhp_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnHLPhp.ItemClick
        ChangeLanguage(Language.PHP)
    End Sub

    Private Sub mnuEditUndo_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles mnuEditUndo.ItemClick
        If ftxtPattern.Focused Then
            ftxtPattern.Undo()
        End If
    End Sub

    Private Sub mnuEditRedo_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles mnuEditRedo.ItemClick
        If ftxtPattern.Focused Then
            ftxtPattern.Redo()
        End If
    End Sub

    Private Sub cboHistory_EditValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboHistory.EditValueChanged
        ftxtPattern.Text = cboHistory.EditValue
    End Sub

    Private Sub mnuViewReset_ItemClick(ByVal sender As System.Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles mnuViewReset.ItemClick
        Me.Left = 10
        Me.Top = 10
        Me.Width = 640
        Me.Height = 480
        bmDefault.RestoreLayoutFromStream(OriginalBarStatus)
        dmMain.RestoreLayoutFromStream(OriginalDockStatus)
    End Sub
End Class
